package cn.car.carguanli.service;

import cn.car.carguanli.util.LocationUtil;
import cn.car.carguanli.websocket.LocationWebSocket;
import cn.car.carguanli.websocket.LocationWebSocket.LocationRecord;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class LocationMonitorService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private LocationWebSocket locationWebSocket;
    
    // 时间窗口：7秒
    private static final long TIME_WINDOW_MS = 7000;
    
    // 距离阈值：用于判断两个车辆是否靠近（50米内认为车辆靠近）
    private static final double PROXIMITY_THRESHOLD = 50.0;
    
    // 极度接近阈值：两车距离非常近，直接触发警报
    private static final double CRITICAL_DISTANCE = 10.0; // 10米以内认为极度接近
    
    // 解除警报的距离阈值（比接近阈值大一些，避免频繁触发警报和解除警报）
    private static final double SAFE_DISTANCE_THRESHOLD = 60.0; // 60米以上认为安全
    
    // 显示距离的阈值：小于等于此距离才显示具体距离
    private static final double SHOW_DISTANCE_THRESHOLD = 100.0; // 100米以内显示距离
    
    // 快速逼近阈值：两车距离变化速度（米/秒）
    private static final double APPROACH_SPEED_THRESHOLD = 5.0; // 5米/秒以上视为快速逼近
    
    // 警报状态过期时间（2分钟）
    private static final long ALERT_EXPIRE_TIME_MS = 2 * 60 * 1000;
    
    // 警报重复间隔（0.5秒）- 减少间隔时间，使警报更频繁
    private static final long ALERT_REPEAT_INTERVAL_MS = 500;
    
    // 距离信息发送间隔（2秒）
    private static final long DISTANCE_INFO_INTERVAL_MS = 2000;

    // 存储当前处于警报状态的ID对及其最后更新时间
    private final Map<String, Long> alertStatus = new HashMap<>();
    
    // 存储最后一次发送警报的时间
    private final Map<String, Long> lastAlertTime = new HashMap<>();
    
    // 存储每对ID的警报计数
    private final Map<String, Integer> alertCounter = new HashMap<>();
    
    // 存储最后一次发送距离信息的时间
    private final Map<String, Long> lastDistanceInfoTime = new HashMap<>();

    @Scheduled(fixedDelay = 1000) // 每1秒执行一次
    public void monitorLocations() {
        Set<String> keys = redisTemplate.keys("location:*");
        if (keys == null || keys.isEmpty()) {
            return;
        }

        // 获取所有位置键
        List<String> locationKeys = new ArrayList<>(keys);
        
        // 检查车辆之间在7秒内的快速逼近情况
        checkRapidApproachWithinTimeWindow(locationKeys);
        
        // 清理过期的警报状态和距离信息时间戳
        cleanExpiredAlerts();
    }
    
    /**
     * 清理过期的警报状态和距离信息时间戳
     */
    private void cleanExpiredAlerts() {
        long currentTime = System.currentTimeMillis();
        Set<String> expiredKeys = new HashSet<>();
        
        for (Map.Entry<String, Long> entry : alertStatus.entrySet()) {
            if (currentTime - entry.getValue() > ALERT_EXPIRE_TIME_MS) {
                expiredKeys.add(entry.getKey());
            }
        }
        
        for (String key : expiredKeys) {
            alertStatus.remove(key);
            lastAlertTime.remove(key);
            alertCounter.remove(key);
            lastDistanceInfoTime.remove(key);
            log.info("警报状态已过期并清除：{}", key);
        }
    }
    
    private void checkRapidApproachWithinTimeWindow(List<String> locationKeys) {
        // 获取所有ID的历史位置数据
        Map<String, List<LocationRecord>> allLocationHistory = new HashMap<>();
        
        for (String key : locationKeys) {
            String id = key.substring("location:".length());
            
            // 获取位置历史数据
            List<Object> locationData = redisTemplate.opsForList().range(key, 0, -1);
            if (locationData == null || locationData.isEmpty()) {
                continue;
            }
            
            // 解析位置数据
            List<LocationRecord> records = new ArrayList<>();
            for (Object data : locationData) {
                try {
                    LocationRecord record = JSON.parseObject(data.toString(), LocationRecord.class);
                    records.add(record);
                } catch (Exception e) {
                    log.error("解析位置记录失败: {}", e.getMessage());
                }
            }
            
            // 按时间戳排序
            records.sort(Comparator.comparingLong(LocationRecord::getTimestamp));
            
            // 获取当前时间
            long currentTime = System.currentTimeMillis();
            
            // 找到时间窗口内的记录
            List<LocationRecord> recentRecords = records.stream()
                    .filter(record -> currentTime - record.getTimestamp() <= TIME_WINDOW_MS)
                    .collect(Collectors.toList());
            
            if (!recentRecords.isEmpty()) {
                allLocationHistory.put(id, recentRecords);
            }
        }
        
        // 检查任意两个ID之间在7秒内是否有快速逼近的情况
        for (Map.Entry<String, List<LocationRecord>> entry1 : allLocationHistory.entrySet()) {
            String id1 = entry1.getKey();
            List<LocationRecord> records1 = entry1.getValue();
            
            for (Map.Entry<String, List<LocationRecord>> entry2 : allLocationHistory.entrySet()) {
                String id2 = entry2.getKey();
                List<LocationRecord> records2 = entry2.getValue();
                
                // 跳过相同的ID
                if (id1.equals(id2)) {
                    continue;
                }
                
                // 检查这两个ID在7秒内是否有快速逼近的情况
                ApproachResult approachResult = checkApproachStatus(records1, records2);
                
                // 确保每对ID只有一个警报
                String pairKey = id1.compareTo(id2) < 0 ? 
                                "approach:" + id1 + ":" + id2 : 
                                "approach:" + id2 + ":" + id1;
                
                long currentTime = System.currentTimeMillis();
                double currentDistance = approachResult.getCurrentDistance();
                
                // 记录当前位置和时间戳，用于日志调试
                LocationRecord lastRecord1 = records1.get(records1.size() - 1);
                LocationRecord lastRecord2 = records2.get(records2.size() - 1);
                
                // 修复日志格式，使用正确的方式记录浮点数
                log.debug("ID {} 和 ID {} 当前距离: {}米, 接近速度: {}米/秒, 是否快速逼近: {}, 是否极近: {}", 
                         id1, id2, 
                         String.format("%.2f", currentDistance), 
                         String.format("%.2f", approachResult.getApproachSpeed()),
                         approachResult.isRapidApproach(),
                         approachResult.isCriticalDistance());
                
                // 强制记录更详细的调试信息，帮助排查问题
                log.info("检测结果 - ID {} 和 ID {}: 距离={}米, 速度={}米/秒, 快速逼近={}, 距离极近={}, 正在接近={}, 安全距离={}", 
                        id1, id2, 
                        String.format("%.2f", currentDistance),
                        String.format("%.2f", approachResult.getApproachSpeed()),
                        approachResult.isRapidApproach(),
                        approachResult.isCriticalDistance(),
                        approachResult.isStillApproaching(),
                        approachResult.isSafeDistance());
                
                // 检查安全距离是否为 false（距离不安全）
                if (!approachResult.isSafeDistance()) {
                    // 更新警报状态时间戳
                    alertStatus.put(pairKey, currentTime);
                    
                    // 检查是否需要发送警报（首次警报或达到重复间隔）
                    Long lastAlert = lastAlertTime.get(pairKey);
                    boolean shouldSendAlert = lastAlert == null || (currentTime - lastAlert >= ALERT_REPEAT_INTERVAL_MS);
                    
                    // 记录警报触发条件
                    log.info("安全距离警报触发 - ID {} 和 ID {}: 应该发送警报={}, 上次警报时间={}, 当前时间={}, 时间差={}ms", 
                            id1, id2, shouldSendAlert, lastAlert, currentTime, 
                            lastAlert != null ? (currentTime - lastAlert) : "N/A");
                    
                    if (shouldSendAlert) {
                        // 增加警报计数
                        int count = alertCounter.getOrDefault(pairKey, 0) + 1;
                        alertCounter.put(pairKey, count);
                        
                        // 警报消息
                        String alertMessage = "警告：检测到车辆距离不安全，请注意安全！";
                        
                        // 在日志中记录详细信息
                        log.warn("警告：ID {} 和 ID {} 距离不安全（当前距离{}米），请注意安全！(警报次数: {})",
                                id1, id2,
                                String.format("%.2f", currentDistance), 
                                count);
                        
                        lastAlertTime.put(pairKey, currentTime);
                        try {
                            // 发送警报消息
                            locationWebSocket.broadcastMessage(alertMessage);
                            log.info("成功发送安全距离警报消息：{}", alertMessage);
                        } catch (Exception e) {
                            log.error("发送安全距离警报消息失败", e);
                        }
                    }
                } 
                // 旧的判断逻辑保持不变，以保证原有功能正常
                // 只在距离小于等于50米时处理警报逻辑
                else if (currentDistance <= PROXIMITY_THRESHOLD) {
                    if (approachResult.isRapidApproach() || approachResult.isCriticalDistance()) {
                        // 更新警报状态时间戳
                        alertStatus.put(pairKey, currentTime);
                        
                        // 检查是否需要发送警报（首次警报或达到重复间隔）
                        Long lastAlert = lastAlertTime.get(pairKey);
                        boolean shouldSendAlert = lastAlert == null || (currentTime - lastAlert >= ALERT_REPEAT_INTERVAL_MS);
                        
                        // 记录警报触发条件
                        log.info("警报触发 - ID {} 和 ID {}: 应该发送警报={}, 上次警报时间={}, 当前时间={}, 时间差={}ms", 
                                id1, id2, shouldSendAlert, lastAlert, currentTime, 
                                lastAlert != null ? (currentTime - lastAlert) : "N/A");
                        
                        if (shouldSendAlert) {
                            // 增加警报计数
                            int count = alertCounter.getOrDefault(pairKey, 0) + 1;
                            alertCounter.put(pairKey, count);
                            
                            String alertReason = approachResult.isCriticalDistance() ? 
                                                "距离过近" : "正在快速逼近";
                            
                            // 警报消息不显示ID和距离信息，只显示警告内容
                            String alertMessage = "警告：检测到车辆快速接近，存在碰撞风险！";
                            
                            // 在日志中记录详细信息（包含距离和ID），但不发送给前端
                            log.warn("警告：ID {} 和 ID {} {}（接近速度{}米/秒，当前距离{}米），存在碰撞风险！(警报次数: {})",
                                    id1, id2, alertReason, 
                                    String.format("%.2f", approachResult.getApproachSpeed()), 
                                    String.format("%.2f", currentDistance), 
                                    count);
                            
                            lastAlertTime.put(pairKey, currentTime);
                            try {
                                // 确保消息被发送
                                locationWebSocket.broadcastMessage(alertMessage);
                                log.info("成功发送警报消息：{}", alertMessage);
                            } catch (Exception e) {
                                log.error("发送快速逼近警报消息失败", e);
                            }
                        }
                    } else if (approachResult.isStillApproaching()) {
                        // 如果仍在接近但不是快速逼近
                        if (alertStatus.containsKey(pairKey)) {
                            // 更新警报状态时间戳
                            alertStatus.put(pairKey, currentTime);
                            
                            // 每隔一段时间发送一次提醒
                            Long lastAlert = lastAlertTime.get(pairKey);
                            boolean shouldSendAlert = lastAlert == null || (currentTime - lastAlert >= ALERT_REPEAT_INTERVAL_MS);
                            
                            if (shouldSendAlert) {
                                // 增加警报计数
                                int count = alertCounter.getOrDefault(pairKey, 0) + 1;
                                alertCounter.put(pairKey, count);
                                
                                // 警报消息不显示ID和距离信息，只显示警告内容
                                String alertMessage = "警告：检测到车辆持续接近，请注意安全！";
                                
                                // 在日志中记录详细信息（包含距离和ID），但不发送给前端
                                log.warn("警告：ID {} 和 ID {} 仍在接近中（接近速度{:.2f}米/秒，当前距离{:.2f}米），请注意安全！(警报次数: {})",
                                        id1, id2, approachResult.getApproachSpeed(), 
                                        currentDistance, count);
                                
                                lastAlertTime.put(pairKey, currentTime);
                                try {
                                    locationWebSocket.broadcastMessage(alertMessage);
                                } catch (Exception e) {
                                    log.error("发送接近警报消息失败", e);
                                }
                            }
                        }
                    }
                } else if (approachResult.isSafeDistance() || !approachResult.isApproaching()) {
                    // 如果距离已经安全或不再接近，且之前有警报，发送解除警报消息
                    if (alertStatus.containsKey(pairKey)) {
                        String reason = approachResult.isSafeDistance() ? 
                                "距离已恢复安全" : "不再相互接近";
                        
                        // 获取总警报次数
                        int totalAlerts = alertCounter.getOrDefault(pairKey, 0);
                        
                        // 解除警报消息不显示ID和距离信息，只显示解除警报内容
                        String clearMessage = "通知：车辆已安全分离，解除警报。";
                        
                        // 在日志中记录详细信息（包含距离和ID），但不发送给前端
                        log.info("通知：ID {} 和 ID {} {}，当前距离{:.2f}米，解除警报。(总警报次数: {})",
                                id1, id2, reason, currentDistance, totalAlerts);
                        
                        alertStatus.remove(pairKey);
                        lastAlertTime.remove(pairKey);
                        alertCounter.remove(pairKey);
                        try {
                            locationWebSocket.broadcastMessage(clearMessage);
                        } catch (Exception e) {
                            log.error("发送解除快速逼近警报消息失败", e);
                        }
                    }
                }
            }
        }
    }
    
    /**
     * 检查两个ID在7秒时间窗口内的快速逼近状态
     */
    private ApproachResult checkApproachStatus(List<LocationRecord> records1, List<LocationRecord> records2) {
        // 如果任一记录少于2个点，无法计算逼近速度
        if (records1.size() < 2 || records2.size() < 2) {
            // 如果有足够的记录计算当前距离，则计算距离
            if (!records1.isEmpty() && !records2.isEmpty()) {
                LocationRecord lastRecord1 = records1.get(records1.size() - 1);
                LocationRecord lastRecord2 = records2.get(records2.size() - 1);
                double currentDistance = LocationUtil.calculateDistance(
                    lastRecord1.getLatitude(), lastRecord1.getLongitude(),
                    lastRecord2.getLatitude(), lastRecord2.getLongitude()
                );
                
                // 如果距离极近，即使无法计算速度也认为需要警报
                boolean isCriticalDistance = currentDistance <= CRITICAL_DISTANCE;
                
                return new ApproachResult(false, false, currentDistance > SAFE_DISTANCE_THRESHOLD, 
                                         isCriticalDistance, 0, currentDistance);
            } else {
                // 否则使用默认的最大距离
                return new ApproachResult(false, false, true, false, 0, Double.MAX_VALUE);
            }
        }
        
        // 获取两组记录的最早和最晚的时间点
        LocationRecord firstRecord1 = records1.get(0);
        LocationRecord lastRecord1 = records1.get(records1.size() - 1);
        LocationRecord firstRecord2 = records2.get(0);
        LocationRecord lastRecord2 = records2.get(records2.size() - 1);
        
        // 计算初始距离和当前距离
        double initialDistance = LocationUtil.calculateDistance(
            firstRecord1.getLatitude(), firstRecord1.getLongitude(),
            firstRecord2.getLatitude(), firstRecord2.getLongitude()
        );
        
        double currentDistance = LocationUtil.calculateDistance(
            lastRecord1.getLatitude(), lastRecord1.getLongitude(),
            lastRecord2.getLatitude(), lastRecord2.getLongitude()
        );
        
        // 判断是否距离极近（小于等于10米）
        boolean isCriticalDistance = currentDistance <= CRITICAL_DISTANCE;
        
        // 计算距离变化（正值表示距离减小，即在接近）
        double distanceChange = initialDistance - currentDistance;
        
        // 判断是否在接近
        boolean isApproaching = distanceChange > 0;
        
        // 如果距离在增加，则不是在逼近
        if (!isApproaching && !isCriticalDistance) {
            return new ApproachResult(false, false, currentDistance > SAFE_DISTANCE_THRESHOLD, 
                                     isCriticalDistance, 0, currentDistance);
        }
        
        // 计算时间间隔（使用两组记录中的最大时间差）
        long timeInterval = Math.max(
            lastRecord1.getTimestamp() - firstRecord1.getTimestamp(),
            lastRecord2.getTimestamp() - firstRecord2.getTimestamp()
        );
        
        // 避免除以零
        if (timeInterval <= 0) {
            return new ApproachResult(false, false, currentDistance > SAFE_DISTANCE_THRESHOLD, 
                                     isCriticalDistance, 0, currentDistance);
        }
        
        // 计算逼近速度（米/秒）
        double approachSpeed = distanceChange / (timeInterval / 1000.0);
        
        // 判断是否是快速逼近（速度超过阈值且当前距离小于接近阈值）
        boolean isRapidApproach = approachSpeed >= APPROACH_SPEED_THRESHOLD && currentDistance <= PROXIMITY_THRESHOLD;
        
        // 判断是否仍在接近但不是快速逼近
        boolean isStillApproaching = isApproaching && !isRapidApproach && !isCriticalDistance;
        
        // 判断是否安全距离
        boolean isSafeDistance = currentDistance > SAFE_DISTANCE_THRESHOLD;
        
        return new ApproachResult(isRapidApproach, isStillApproaching, isSafeDistance, 
                                 isCriticalDistance, approachSpeed, currentDistance);
    }
    
    /**
     * 快速逼近结果类
     */
    private static class ApproachResult {
        private final boolean rapidApproach;     // 是否快速逼近
        private final boolean stillApproaching;  // 是否仍在接近（但不是快速逼近）
        private final boolean safeDistance;      // 是否安全距离
        private final boolean criticalDistance;  // 是否距离极近
        private final double approachSpeed;      // 逼近速度（米/秒）
        private final double currentDistance;    // 当前距离
        
        public ApproachResult(boolean rapidApproach, boolean stillApproaching, boolean safeDistance, 
                            boolean criticalDistance, double approachSpeed, double currentDistance) {
            this.rapidApproach = rapidApproach;
            this.stillApproaching = stillApproaching;
            this.safeDistance = safeDistance;
            this.criticalDistance = criticalDistance;
            this.approachSpeed = approachSpeed;
            this.currentDistance = currentDistance;
        }
        
        public boolean isRapidApproach() { return rapidApproach; }
        public boolean isStillApproaching() { return stillApproaching; }
        public boolean isSafeDistance() { return safeDistance; }
        public boolean isCriticalDistance() { return criticalDistance; }
        public boolean isApproaching() { return rapidApproach || stillApproaching; }
        public double getApproachSpeed() { return approachSpeed; }
        public double getCurrentDistance() { return currentDistance; }
    }
} 